﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.ServiceModel;
using System.Xml.Linq;
using Domain.Model;
using ExpressionSerialization;

namespace Northwind
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class NorthwindService : IQueryService
    {
        private static readonly Uri BaseAddress = new Uri("http://localhost:8999/");
        private readonly Assembly[] _assemblies;
        private readonly QueryCreator _creator;
        private readonly TypeResolver _resolver;
        private readonly ExpressionSerializer _serializer;

        public NorthwindService()
        {
            _creator = new QueryCreator(FnGetContextObjects);
            _assemblies = new[]
                             {
                                 typeof (Customer).Assembly, typeof (ExpressionType).Assembly,
                                 typeof (IQueryable).Assembly
                                 , typeof (int).Assembly
                             };
            _resolver = new TypeResolver(_assemblies, new[]
                                                        {
                                                            typeof (Customer), typeof (Product),
                                                        });
            CustomExpressionXmlConverter queryconverter = new QueryExpressionXmlConverter(_creator, _resolver);
            CustomExpressionXmlConverter knowntypeconverter = new KnownTypeExpressionXmlConverter(_resolver);
            _serializer = new ExpressionSerializer(_resolver, new[] {queryconverter, knowntypeconverter});
        }

        #region IQueryService Members

        public object ExecuteQuery(XElement xml)
        {
            Expression e = _serializer.Deserialize(xml);
            var m = (MethodCallExpression) e;

            LambdaExpression lambda = Expression.Lambda(m);
            Delegate fn = lambda.Compile();
            dynamic result = fn.DynamicInvoke(new object[0]);
            dynamic arr = result;
            if (result is IEnumerable)
            {
                arr = Enumerable.ToArray(result);
            }
            return arr;
        }

        #endregion

        private static dynamic FnGetContextObjects(Type elementType)
        {
            var context = new Context();
            return context.Set(elementType);
        }

        private static void Main()
        {
            var q = new Customer();
            using (var host = new ServiceHost(typeof (NorthwindService)))
            {
                host.Open();
                string baseAddress = host.BaseAddresses.FirstOrDefault().AbsoluteUri ?? "not in .config file";
                Console.WriteLine("Service {0} started.\n\tAddress:\n\t{1}", typeof (NorthwindService).FullName,
                                  baseAddress);
                Test();
                Console.ReadLine();
            }
        }


        private static void Test()
        {
            var client = new WebHttpClient<IQueryService>(BaseAddress);
            IQueryProvider provider = new RemoteProvider(client);
            var query = new Query<Customer>(provider);
            IQueryable<Customer> queryable = from c in query
                                             where c.ID > 5 && c.ID < 10
                                             select c;


            List<Customer> results = queryable.ToList();
            Console.WriteLine(queryable.Count());
            Customer qqq = query.OrderBy(a => a.ID).Skip(2).Take(3).FirstOrDefault();
        }
    }
}